/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.inspur.edp.lcm.metadata.core.persistence;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.inspur.edp.lcm.metadata.api.entity.MetadataPackageHeader;
import com.inspur.edp.lcm.metadata.api.entity.MetadataPackageVersion;
import com.inspur.edp.lcm.metadata.api.entity.MetadataProject;
import com.inspur.edp.lcm.metadata.api.entity.ProjectHeader;
import com.inspur.edp.lcm.metadata.api.mvnEntity.MavenPackageRefs;
import com.inspur.edp.lcm.metadata.common.FileServiceImp;
import com.inspur.edp.lcm.metadata.common.Utils;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

public class MetadataProjectRepository {
    private FileServiceImp fileService = new FileServiceImp();

    public void add(String fullPath, String projectStr) {
        fileService.createFile(fullPath);
        fileService.fileUpdate(fullPath, projectStr, false);
    }

    public MetadataProject assmbllyMetadataProj(String projName, String projNameSpace, String packageName) {
        MetadataProject metadataProject = new MetadataProject();
        metadataProject.setId(UUID.randomUUID().toString());
        metadataProject.setNameSpace(projNameSpace);
        metadataProject.setName(projName);
        metadataProject.setMetadataPackageInfo(new MetadataPackageHeader());
        metadataProject.getMetadataPackageInfo().setName(packageName);
        metadataProject.getMetadataPackageInfo().setVersion(new MetadataPackageVersion("1.0.0"));
        metadataProject.setMetadataPackageRefs(new ArrayList<>());
        metadataProject.setMavenPackageRefs(new ArrayList<>());
        return metadataProject;
    }

    public void setSourceData(String absolutePath,
        Map<String, Map<String, Map<String, Long>>> sourceData) throws IOException {
        String filePath = absolutePath + "/" + Utils.getMetadataProjPath() + "/" + Utils.getMetadataBinPath() + "/" + Utils.getSourceDataFileName();
        File sourceDataFile = new File(filePath);
        if (!sourceDataFile.exists()) {
            createBinDir(absolutePath);
            sourceDataFile.createNewFile();
        }
        fileService.fileUpdate(filePath, JSON.toJSONString(sourceData, true), false);
    }

    public Map<String, Map<String, Map<String, Long>>> getSourceData(String absolutePath) {
        Map<String, Map<String, Map<String, Long>>> sourceData = new HashMap<>();
        String filePath = absolutePath + "/" + Utils.getMetadataProjPath()
            + "/" + Utils.getMetadataBinPath() + "/" + Utils.getSourceDataFileName();
        if (!fileService.isFileExist(filePath)) {
            Map<String, Map<String, Long>> modifiedTime = new HashMap<>();
            modifiedTime.put("metadata", new HashMap<>());
            modifiedTime.put("api_src", new HashMap<>());
            modifiedTime.put("api", new HashMap<>());
            modifiedTime.put("comp", new HashMap<>());
            modifiedTime.put("runtime", new HashMap<>());
            sourceData.put("modifiedtime", modifiedTime);
            return sourceData;
        }
        try {
            // 如果sourcedata.json文件格式错误，则初始化，并返回空。
            sourceData = JSONObject.parseObject(fileService.fileRead(filePath), new TypeReference<Map<String, Map<String, Map<String, Long>>>>() {
            });
            return sourceData;
        } catch (Exception e) {
            try {
                fileService.fileDelete(filePath);
                return getSourceData(absolutePath);
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        }
        return sourceData;
    }

    public void setMavenUpdateFlag(String absolutePath, Boolean flag) {
        String filePath = absolutePath + "/" + Utils.getMetadataProjPath()
            + "/" + Utils.getMetadataBinPath() + "/" + Utils.getMavenUpdateFlagFileName();
        File mavenUpdateFlagFile = new File(filePath);
        if (!mavenUpdateFlagFile.exists()) {
            createBinDir(absolutePath);
            try {
                mavenUpdateFlagFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        String feature = flag ? " -U" : "";
        fileService.fileUpdate(filePath, feature, false);
    }

    public String getMavenUpdateFlag(String absolutePath) {
        String filePath = absolutePath + "/" + Utils.getMetadataProjPath()
            + "/" + Utils.getMetadataBinPath() + "/" + Utils.getMavenUpdateFlagFileName();
        if (!fileService.isFileExist(filePath)) {
            setMavenUpdateFlag(absolutePath, false);
        }
        String updateFlag = fileService.fileRead(filePath);
        return updateFlag;
    }

    private void createBinDir(String absolutePath) {
        String dirPath = absolutePath + "/" + Utils.getMetadataProjPath()
            + "/" + Utils.getMetadataBinPath();
        File binDir = new File(dirPath);
        if (!binDir.exists()) {
            binDir.mkdirs();
        }
    }

    public void updateMetadataProject(String projPath, MetadataProject metadataProject) {
        String filePath = getMdprojPath(projPath, metadataProject);
        Utils.writeValue(filePath, metadataProject);
    }

    public void updateRefs(String projPath, MetadataProject metadataProject, MetadataPackageHeader packageHeader) {
        String filePath = getMdprojPath(projPath, metadataProject);

        if (packageHeader.getName() == null || packageHeader.getName().isEmpty()) {
            return;
        }

        if (packageHeader.getName().endsWith(Utils.getMetadataPackageExtension())) {
            packageHeader.setName(fileService.getFileNameWithoutExtension(packageHeader.getName()));
        }

        if (metadataProject.getMetadataPackageRefs() == null) {
            metadataProject.setMetadataPackageRefs(new ArrayList<>());
        }

        boolean existFlag = metadataProject.getMetadataPackageRefs().stream().anyMatch(ref -> ref.getName().equals(packageHeader.getName()));
        if (!existFlag) {
            metadataProject.getMetadataPackageRefs().add(packageHeader);
            Utils.writeValue(filePath, metadataProject);
        }
    }

    public void removeRefs(String projPath, MetadataProject metadataProject, MetadataPackageHeader packageHeader) {
        String filePath = getMdprojPath(projPath, metadataProject);

        if (packageHeader.getName() == null || packageHeader.getName().isEmpty()) {
            return;
        }

        if (packageHeader.getName().endsWith(Utils.getMetadataPackageExtension())) {
            packageHeader.setName(fileService.getFileNameWithoutExtension(packageHeader.getName()));
        }

        if (metadataProject.getMetadataPackageRefs() == null) {
            metadataProject.setMetadataPackageRefs(new ArrayList<>());
        }

        MetadataPackageHeader metadataPackageHeader = metadataProject.getMetadataPackageRefs()
            .stream()
            .filter(ref -> ref.getName().equals(packageHeader.getName()))
            .findFirst().orElse(null);
        if (metadataPackageHeader != null) {
            metadataProject.getMetadataPackageRefs().remove(metadataPackageHeader);
            Utils.writeValue(filePath, metadataProject);
        }
    }

    public void updateMavenRefs(String projPath, MetadataProject metadataProject, MavenPackageRefs packageRefs) {
        String filePath = getMdprojPath(projPath, metadataProject);

        if (metadataProject.getMavenPackageRefs() == null) {
            metadataProject.setMavenPackageRefs(new ArrayList<>());
        }

        int index = -1;
        for (int i = 0; i < metadataProject.getMavenPackageRefs().size(); i++) {
            if (metadataProject.getMavenPackageRefs().get(i).getGroupId().equals(packageRefs.getGroupId()) && metadataProject.getMavenPackageRefs().get(i).getArtifactId().equals(packageRefs.getArtifactId())) {
                index = i;
                break;
            }
        }
        if (index != -1) {
            metadataProject.getMavenPackageRefs().get(index).setVersion(packageRefs.getVersion());
        } else {
            metadataProject.getMavenPackageRefs().add(packageRefs);
        }

        Utils.writeValue(filePath, metadataProject);
    }

    public void removeMavenRefs(String projPath, MetadataProject metadataProject, MavenPackageRefs packageRefs) {
        String filePath = getMdprojPath(projPath, metadataProject);

        if (metadataProject.getMavenPackageRefs() == null) {
            metadataProject.setMavenPackageRefs(new ArrayList<>());
        }

        MavenPackageRefs mavenPackageRefs = metadataProject.getMavenPackageRefs()
            .stream()
            .filter(ref -> ref.getGroupId().equals(packageRefs.getGroupId()) && ref.getArtifactId().equals(packageRefs.getArtifactId()))
            .findFirst()
            .orElse(null);
        if (mavenPackageRefs != null) {
            metadataProject.getMavenPackageRefs().remove(mavenPackageRefs);
            Utils.writeValue(filePath, metadataProject);
        }
    }

    public void removeProjectRefs(String projPath, MetadataProject metadataProject, List<String> mdpkgNames) {
        String filePath = getMdprojPath(projPath, metadataProject);
        if (metadataProject.getProjectRefs() == null) {
            metadataProject.setProjectRefs(new ArrayList<>());
        }

        List<ProjectHeader> projectHeaders = metadataProject.getProjectRefs()
            .stream()
            .filter(ref -> mdpkgNames.contains(ref.getName()))
            .collect(Collectors.toList());
        if (projectHeaders != null && projectHeaders.size() > 0) {
            for (ProjectHeader header : projectHeaders) {
                metadataProject.getProjectRefs().remove(header);
            }
            Utils.writeValue(filePath, metadataProject);
        }
    }

    public void updateProjectRefs(String projPath, MetadataProject metadataProject, ProjectHeader projectHeader) {
        String filePath = getMdprojPath(projPath, metadataProject);
        if (metadataProject.getProjectRefs() == null) {
            metadataProject.setProjectRefs(new ArrayList<>());
        }
        boolean notExistFlag = metadataProject.getProjectRefs()
            .stream()
            .noneMatch(ref -> projectHeader.getName().equals(ref.getName()));
        if (notExistFlag) {
            metadataProject.getProjectRefs().add(projectHeader);
            Utils.writeValue(filePath, metadataProject);
        }
    }

    private String getMdprojPath(String projPath, MetadataProject metadataProject) {
        String mdprojPath = Paths.get(projPath).resolve(Utils.getMetadataProjPath() + File.separator + metadataProject.getProjectFileName()).toString();
        return mdprojPath;
    }
}
